//
// Created by denglibin on 2021/3/26.
// 使用render渲染，不直接使用surface
#include <SDL.h>
#include <avformat.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#define FRAME_RATE 30 //帧率
int WIN_WIDTH =  1440;
int WIN_HEIGHT = 900;


static struct SDL_Window * win; //窗口
static struct SDL_Surface * winSurface; //窗口的surface
static struct SDL_Renderer * winRender;//窗口渲染器
static SDL_Texture*  texture = NULL;

static void draw(uint8_t* data, int pitch){
    SDL_UpdateTexture(texture, NULL, data, pitch);//更新纹理画面
    SDL_RenderClear(winRender);
    int r = SDL_RenderCopy(winRender, texture, NULL, NULL);
    if (r != 0){
        SDL_Log("Can not copy render %s\n", SDL_GetError());
        exit(1);
    }
    //生效
    SDL_RenderPresent(winRender);
}
int playFile(const char* filePath, enum file_type type){
    //av_register_all();
    AVFormatContext * pContext = NULL;

    //1
    AVDictionary* opt = NULL;
    if(type == NET){
        avformat_network_init();
        av_dict_set(&opt, "rtsp_transport", "tcp", 0);
        av_dict_set(&opt, "max_delay", "550", 0);
    }

    //打开输入流
    int ret = avformat_open_input(&pContext, filePath, NULL, &opt);
    if(ret){
        av_log(NULL, AV_LOG_INFO, "%s:%s", "[ERROR]=>avformat_open_input error!\n", filePath);
        return 1;
    }
    av_log(NULL, AV_LOG_INFO, "avformat_open_input success!\n");

    //2 寻找流信息 H264 宽高
    ret = avformat_find_stream_info(pContext, NULL);
    if(ret){
        av_log(NULL, AV_LOG_ERROR, "%s:%s", "[ERROR]=>avformat_find_stream_info error!\n", filePath);
        return 1;
    }
    av_log(NULL, AV_LOG_INFO, "avformat_find_stream_info success!\n");

    int time = pContext->duration;
    int min = (time / 1000000) / 60; //分钟
    int sec = (time / 1000000) % 60; //秒
    av_log(NULL, AV_LOG_INFO, "%d分%d秒\n", min, sec);
    //显示一下
    av_dump_format(pContext, 0, filePath, 0);

    //寻找流
    int videoStream = -1, audioStream = -1;
    //查找视频流
    videoStream = av_find_best_stream(pContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    // audioStream = av_find_best_stream(pContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

    //找解码器
    AVCodec* vCodec = avcodec_find_decoder(pContext->streams[videoStream]->codecpar->codec_id);
    if(!vCodec){
        av_log(NULL, AV_LOG_ERROR, "%s:%s\n", "[ERROR]=>avcodec_find_decoder error!", filePath);
        return -1;
    }
    av_log(NULL, AV_LOG_INFO, "%s\n", "[INFO]=>avcodec_find_decoder success!");

    AVCodecContext* avCodecContext = avcodec_alloc_context3(vCodec);
    avcodec_parameters_to_context(avCodecContext, pContext->streams[videoStream]->codecpar);


    //打开解码器
    ret = avcodec_open2(avCodecContext, vCodec,  NULL);
    if(ret != 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "[ERROR]=>avcodec_open2 error!");
        return -1;
    }
    av_log(NULL, AV_LOG_INFO, "%s\n", "[INFO]=>avcodec_open2 success!");

    //****计算输入流信息****//
    AVRational frame_rate = av_guess_frame_rate(pContext, pContext->streams[videoStream], NULL); //帧率
    av_log(NULL, AV_LOG_INFO, "帧率:%d/%d\n", frame_rate.num, frame_rate.den);


    //开辟一块内存空间
    AVFrame* frame = av_frame_alloc();
    //创建一个yuv420视频像素数据格式缓冲区(一帧数据)
    AVFrame* avFrame_yuv420p = av_frame_alloc();

    //分配空间，进行图像转换
    int width = pContext->streams[videoStream]->codecpar->width;
    int height = pContext->streams[videoStream]->codecpar->height;
    int format = pContext->streams[videoStream]->codecpar->format;

    int nSize = av_image_get_buffer_size((enum AVPixelFormat)format, width, height, 1); //通过指定像素格式、图像宽、图像高来计算所需的内存大小
    av_log(NULL, AV_LOG_INFO, "空间大小:%d\n", nSize);
    //开辟一块内存空间
    uint8_t * buf = (uint8_t*)av_malloc(nSize);
    // 向avFrame_yuv420p->填充数据
    //参数一：目标->填充数据(avframe_yuv420p)
    //参数二：目标->每一行大小
    //参数三：原始数据
    //参数四：目标->格式类型
    //参数五：宽
    //参数六：高
    //参数七：字节对齐方式
    av_image_fill_arrays(avFrame_yuv420p->data,
                         avFrame_yuv420p->linesize,
                         buf,
                         AV_PIX_FMT_YUV420P,
                         avCodecContext->width,
                         avCodecContext->height,
                         1);

    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));

    //转换上下文
    struct SwsContext * swsContext = sws_getContext(width, height, (enum AVPixelFormat)format,
                                                    width, height, AV_PIX_FMT_YUV420P, SWS_BICUBIC,
                                                    NULL, NULL, NULL);

    // 将yuv420p数据写入.yuv文件中
    const char *outfile = "mp4.yuv";
    FILE* file_yuv420p = fopen(outfile,"wb+");
    if (file_yuv420p ==NULL){
        av_log(NULL, AV_LOG_ERROR, "%s\n:%s", "输出文件打开失败", outfile);
        return 1;
    }
    char errBuf[1024];
    //读帧
    int frameCount = 0;
    int y_size, u_size, v_size;
    y_size = avCodecContext->width * avCodecContext->height;
    u_size = y_size/4;
    v_size = y_size/4;
    SDL_Event  event;
    texture = SDL_CreateTexture(winRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, avCodecContext->width, avCodecContext->height);
    while ( av_read_frame(pContext, packet) >= 0){ //读到了
        SDL_PollEvent(&event);
        if(event.type == SDL_QUIT){
            exit(0);
        }
        //判断stream_index
        if(packet->stream_index == AVMEDIA_TYPE_VIDEO){
            //解码
            //解码一帧压缩数据->得到视频像素数据->yuv格式
            //发送一帧视频压缩数据
            ret = avcodec_send_packet(avCodecContext, packet);
            if(ret != 0){
                av_log(NULL, AV_LOG_ERROR, "%s\n", "[ERROR]=>avcodec_send_packet error!");
                return -1;
            }
            //解码一帧视频压缩数据->进行解码(作用：用于解码操作)
            ret = avcodec_receive_frame(avCodecContext, frame);
            if(ret != 0){
                if(ret == AVERROR(EAGAIN)){
                    continue;
                }
                av_strerror(ret, errBuf, 1024);
                av_log(NULL, AV_LOG_ERROR, "%s:%d:%s\n", "[ERROR]=>avcodec_receive_frame error!",ret, errBuf);
                return -1;
            }
            //解码成功
            //不能够保证解码出来的一帧视频像素数据格式是yuv格式
            //视频像素数据格式很多种类型: yuv420P、yuv422p、yuv444p等等...
            //保证：我的解码后的视频像素数据格式统一为yuv420P->通用的格式
            //进行类型转换: 将解码出来的视频像素点数据格式->统一转类型为yuv420P
            //sws_scale作用：进行类型转换的
            //参数一：视频像素数据格式上下文
            //参数二：原来的视频像素数据格式->输入数据
            //参数三：原来的视频像素数据格式->输入画面每一行大小
            //参数四：原来的视频像素数据格式->输入画面每一行开始位置(填写：0->表示从原点开始读取)
            //参数五：原来的视频像素数据格式->输入数据行数
            //参数六：转换类型后视频像素数据格式->输出数据
            //参数七：转换类型后视频像素数据格式->输出画面每一行大小
            sws_scale(swsContext, (const uint8_t *const *)frame->data,
                      frame->linesize, 0, avCodecContext->height,
                      avFrame_yuv420p->data, avFrame_yuv420p->linesize);
            //方式一：直接显示视频上面去
            draw(avFrame_yuv420p->data[0], avFrame_yuv420p->linesize[0]);

            uint32_t  begin = SDL_GetTicks();
            uint32_t  end = SDL_GetTicks();
            uint32_t  cost = end - begin;
            uint32_t per_frame_time = 1000 / FRAME_RATE;//每一帧耗时ms
            if (per_frame_time > cost){
                //减去循环体耗时，得到还应休眠的时间
                SDL_Delay(per_frame_time - cost);
            }
        }
    }

    //释放内存资源，关闭解码器
    av_packet_free(&packet);
    fclose(file_yuv420p);
    av_frame_free(&frame);
    av_frame_free(&avFrame_yuv420p);
    free(buf);
    avcodec_close(avCodecContext);
    avformat_free_context(pContext);
    return 0;
}







/**
 * 初始化操作
 */
static void init(){
    //初始化，成功返回0
    if (SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Can not init video %s\n", SDL_GetError());
        exit(1);
    }
    // 提升图像质量，否则默认缩放质量会有毛剌
    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");

}



/**
 * 创建显示窗口
 * @return
 */
static int showWindow(){

    win = SDL_CreateWindow("Hello World",
                           SDL_WINDOWPOS_CENTERED,
                           SDL_WINDOWPOS_CENTERED,
                           WIN_WIDTH,
                           WIN_HEIGHT,
                           SDL_WINDOW_SHOWN
    );
    if (win == NULL) {
        SDL_Log("Can not create window %s\n", SDL_GetError());
        return 2;
    }

    winRender = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
    winSurface = SDL_GetWindowSurface(win);
    return 0;

}

/**
 * 清理
 */
static void clean(){
    SDL_DestroyRenderer(winRender);
    SDL_FreeSurface(winSurface);
    SDL_DestroyWindow(win);
}
/**
 * 解码，播放
 * @param argc
 * @param argv
 * @return
 */
int main_win(int argc, char* argv[]){
    init();
    if (showWindow()){
        exit(1);
    }
    playFile("C:\\Users\\mdjro\\Desktop\\12.mp4", LOCAL);
    clean();
    SDL_Quit();
}

